/*!
 * \file CodeHelper.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief 代码辅助工具类，封装了一系列方便使用的代码转换和解析功能
 * 
 * \details 提供全面的金融合约代码处理功能，包括：
 *          - 标准代码格式转换和解析
 *          - 期货、期权、股票等各类金融产品代码处理
 *          - 主力合约、次主力合约代码生成
 *          - 复权代码处理（前复权、后复权）
 *          - 原始代码与标准代码之间的转换
 *          - 代码格式验证和匹配
 *          
 *          支持国内外多个交易所的代码格式，
 *          为整个交易系统提供统一的代码标准化接口，
 *          是WonderTrader框架的核心代码处理模块。
 */
#pragma once
#include "fmtlib.h"
#include "StrUtil.hpp"
#include "../Includes/WTSTypes.h"

#include <boost/xpressive/xpressive_dynamic.hpp>

USING_NS_WTP;

/// \brief 主力合约后缀
static const char* SUFFIX_HOT = ".HOT";
/// \brief 主力合约文件后缀
static const char* FILE_SUF_HOT = "_HOT";

/// \brief 次主力合约后缀
static const char* SUFFIX_2ND = ".2ND";
/// \brief 次主力合约文件后缀
static const char* FILE_SUF_2ND = "_2ND";

/// \brief 前复权合约代码后缀
static const char SUFFIX_QFQ = '-';

/// \brief 后复权合约代码后缀
static const char SUFFIX_HFQ = '+';

/*!
 * \brief 代码辅助工具类
 * 
 * \details 提供静态方法集合，用于处理各种金融产品代码的转换、解析和验证。
 *          支持期货、期权、股票等多种金融产品，提供标准化的代码处理接口。
 *          所有方法都是线程安全的静态方法。
 */
class CodeHelper
{
public:
	/*!\n * \\brief 代码信息结构体\n * \n * \\details 存储解析后的合约代码详细信息，包括：\n *          - 合约代码、交易所代码、产品代码\n *          - 主力合约标记、复权标记\n *          - 各种判断方法和工具函数\n */
	typedef struct _CodeInfo
	{
		char _code[MAX_INSTRUMENT_LENGTH];		///< 合约代码
		char _exchg[MAX_INSTRUMENT_LENGTH];		///< 交易所代码
		char _product[MAX_INSTRUMENT_LENGTH];	///< 产品代码

		//By Wesley @ 2021.12.25
		//去掉合约类型，这里不再进行判断
		//放到CodeHelper类里重构
		//ContractCategory	_category;		//合约类型
		//union
		//{
		//	uint8_t	_hotflag;	//主力标记：0-普通合约1-主力合约2-次主力
		//	uint8_t	_exright;	//是否是复权代码,如SH600000Q: 0-不复权, 1-前复权, 2-后复权
		//};

		/*
		 *	By Wesley @ 2022.03.07
		 *	取消原来的union
		 *	要让主力标记和复权标记分开处理
		 *	因为可能要处理主力合约的复权数据
		 */
		uint8_t	_hotflag;	///< 主力标记：0-普通合约1-主力合约2-次主力
		uint8_t	_exright;	///< 是否是复权代码,如SH600000Q: 0-不复权, 1-前复权, 2-后复权

		/*!\n * \\brief 是否是复权代码\n * \\return 如果是复权代码返回true，否则返回false\n */
		inline bool isExright() const { return _exright != 0; }

		/*!\n * \\brief 是否是前复权代码\n * \\return 如果是前复权代码返回true，否则返回false\n */
		inline bool isForwardAdj() const { return _exright == 1; }

		/*!\n * \\brief 是否是后复权代码\n * \\return 如果是后复权代码返回true，否则返回false\n */
		inline bool isBackwardAdj() const { return _exright == 2; }

		/*!\n * \\brief 是否是主力合约\n * \\return 如果是主力合约返回true，否则返回false\n */
		inline bool isHot() const { return _hotflag ==1; }
		//Ƿ
		inline bool isSecond() const { return _hotflag == 2; }

		/*!\n * \\brief 是否是普通合约\n * \\return 如果是普通合约返回true，否则返回false\n */
		inline bool isFlat() const { return _hotflag == 0; }

		/*!\n * \\brief 获取标准产品ID\n * \\return 返回格式为\"交易所.产品\"的标准产品ID\n * \\note 使用静态缓冲区，线程不安全\n */
		inline const char* stdCommID() const
		{
			static char buffer[64] = { 0 };
			if (strlen(buffer) == 0)
				snprintf(buffer, sizeof(buffer), "%s.%s", _exchg, _product);

			return buffer;
		}

		/*!\n * \\brief 默认构造函数\n * \\details 初始化所有字段为0\n */
		_CodeInfo()
		{
			memset(this, 0, sizeof(_CodeInfo));
			//_category = CC_Future;
		}

		/*!\n * \\brief 清空所有字段\n * \\details 将结构体所有字段重置为0\n */
		inline void clear()
		{
			memset(this, 0, sizeof(_CodeInfo));
		}
	} CodeInfo;

private:
	/*!\n * \\brief 在字符串中查找指定字符\n * \n * \\param src 源字符串\n * \\param symbol 要查找的字符，默认为'.'\n * \\param bReverse 是否反向查找，默认为false\n * \\return 找到的字符位置，未找到返回std::string::npos\n * \n * \\details 提供正向和反向字符查找功能，用于代码解析时的字符定位\n */
	static inline std::size_t find(const char* src, char symbol = '.', bool bReverse = false)
	{
		std::size_t len = strlen(src);
		if (len != 0)
		{
			if (bReverse)
			{
				for (std::size_t idx = len - 1; idx >= 0; idx--)
				{
					if (src[idx] == symbol)
						return idx;
				}
			}
			else
			{
				for (std::size_t idx = 0; idx < len; idx++)
				{
					if (src[idx] == symbol)
						return idx;
				}
			}
		}


		return std::string::npos;
	}

public:
	/*!\n * \\brief 是否是标准期货主力合约代码\n * \n * \\param stdCode 标准代码\n * \\return 如果是主力合约代码返回true，否则返回false\n * \n * \\details 检查代码是否以.HOT结尾，判断是否为主力合约代码\n */
	static inline bool	isStdFutHotCode(const char* stdCode)
	{
		//return StrUtil::endsWith(stdCode, SUFFIX_HOT, false);
		static std::size_t SUF_LEN = strlen(SUFFIX_HOT);
		auto len = strlen(stdCode);
		if (len < SUF_LEN)
			return false;

		return memcmp(stdCode + len - SUF_LEN, SUFFIX_HOT, SUF_LEN) == 0;
	}

	/*!\n * \\brief 是否是标准期货次主力合约代码\n * \n * \\param stdCode 标准代码\n * \\return 如果是次主力合约代码返回true，否则返回false\n * \n * \\details 检查代码是否以.2ND结尾，判断是否为次主力合约代码\n */
	static inline bool	isStdFut2ndCode(const char* stdCode)
	{
		//return StrUtil::endsWith(stdCode, SUFFIX_2ND, false);
		static std::size_t SUF_LEN = strlen(SUFFIX_2ND);
		auto len = strlen(stdCode);
		if (len < SUF_LEN)
			return false;

		return memcmp(stdCode + len - SUF_LEN, SUFFIX_2ND, SUF_LEN) == 0;
	}

	/*!\n * \\brief 是否是期货期权合约代码\n * \n * \\param code 代码字符串\n * \\return 如果是期货期权代码返回true，否则返回false\n * \n * \\details 检查代码格式是否符合期货期权标准，如CFFEX.IO2007.C.4000\n *          使用状态机方式验证代码格式的正确性\n */
	static bool	isStdChnFutOptCode(const char* code)
	{
		/* ʽ */
		//static cregex reg_stk = cregex::compile("^[A-Z]+.[A-z]+\\d{4}.(C|P).\\d+$");	//CFFEX.IO2007.C.4000
		//return 	regex_match(code, reg_stk);
		char state = 0;
		std::size_t i = 0;
		for(; ; i++)
		{
			char ch = code[i];
			if(ch == '\0')
				break;

			if(state == 0)
			{
				if (!('A' <= ch && ch <= 'Z'))
					return false;

				state += 1;
			}
			else if (state == 1)
			{
				if ('A' <= ch && ch <= 'Z')
					continue;

				if (ch == '.')
					state += 1;
				else
					return false;
			}
			else if (state == 2)
			{
				if (!('A' <= ch && ch <= 'z'))
					return false;

				state += 1;
			}
			else if (state == 3)
			{
				if ('A' <= ch && ch <= 'Z')
					continue;

				if ('0' <= ch && ch <= '9')
					state += 1;
				else
					return false;
			}
			else if (state >= 4 && state <= 6)
			{
				if ('0' <= ch && ch <= '9')
					state += 1;
				else
					return false;
			}
			else if (state == 7)
			{
				if (ch == '.')
					state += 1;
				else
					return false;
			}
			else if (state == 8)
			{
				if (ch == 'C' || ch == 'P')
					state += 1;
				else
					return false;
			}
			else if (state == 9)
			{
				if (ch == '.')
					state += 1;
				else
					return false;
			}
			else if (state == 10)
			{
				if ('0' <= ch && ch <= '9')
					state += 1;
				else
					return false;
			}
			else if (state == 11)
			{
				if ('0' <= ch && ch <= '9')
					continue;
				else
					return false;
			}
		}

		return (state == 11);
	}

	/*
	 *	�Ƿ��Ǳ�׼�����ڻ���Լ����
	 *	//CFFEX.IF.2007
	 */
	static inline bool	isStdMonthlyFutCode(const char* code)
	{
		using namespace boost::xpressive;
		/* �����������ʽ */
		static cregex reg_stk = cregex::compile("^[A-Z]+.[A-z]+.\\d{4}$");	//CFFEX.IO.2007
		return 	regex_match(code, reg_stk);
	}

	/*
	 *	��׼����ת��׼Ʒ��ID
	 *	��SHFE.ag.1912->SHFE.ag
	 *	����Ǽ򻯵Ĺ�Ʊ���룬��SSE.600000����ת��SSE.STK
	 */
	static inline std::string stdCodeToStdCommID(const char* stdCode)
	{
		auto idx = find(stdCode, '.', true);
		auto idx2 = find(stdCode, '.', false);
		if(idx != idx2)
		{
			//ǰ������.����ͬһ����˵�������εĴ���
			//��ȡǰ������ΪƷ�ִ���
			return std::string (stdCode, idx);
		}
		else
		{
			//���εĴ��룬ֱ�ӷ���
			//��Ҫ���ĳЩ��������ÿ����Լ�Ľ��׹��򶼲�ͬ�����
			//����������ͰѺ�Լֱ�ӵ���Ʒ������
			return stdCode;
		}
	}

	/*
	 *	�ӻ������º�Լ������ȡ����Ʒ�ִ���
	 *	��ag1912 -> ag
	 *	���ֻ�з����ڻ�Ʒ�ֲ�������
	 *	���������������Լ�Ĵ��봫��������������еĻ����ǵ��õĵط���Bug!
	 */
	static inline std::string rawMonthCodeToRawCommID(const char* code)
	{
		int nLen = 0;
		while ('A' <= code[nLen] && code[nLen] <= 'z')
			nLen++;

		return std::string(code, nLen);
	}

	/*
	 *	�������º�Լ����ת��׼��
	 *	��ag1912ת��ȫ��
	 *	���������������Լ�Ĵ��봫��������������еĻ����ǵ��õĵط���Bug!
	 */
	static inline std::string rawMonthCodeToStdCode(const char* code, const char* exchg, bool isComm = false)
	{
		thread_local static char buffer[64] = { 0 };
		std::size_t len = 0;
		if(isComm)
		{
			len = strlen(exchg);
			memcpy(buffer, exchg, len);
			buffer[len] = '.';
			len += 1;

			auto clen = strlen(code);
			memcpy(buffer+len, code, clen);
			len += clen;
			buffer[len] = '\0';
			len += 1;
		}
		else
		{
			std::string pid = rawMonthCodeToRawCommID(code);
			len = strlen(exchg);
			memcpy(buffer, exchg, len);
			buffer[len] = '.';
			len += 1;

			memcpy(buffer + len, pid.c_str(), pid.size());
			len += pid.size();
			buffer[len] = '.';
			len += 1;

			char* s = (char*)code;
			s += pid.size();
			if (strlen(s) == 4)
			{
				wt_strcpy(buffer + len, s, 4);
				len += 4;
			}
			else
			{
				if (s[0] > '5')
					buffer[len] = '1';
				else
					buffer[len] = '2';
				len += 1;
				wt_strcpy(buffer + len, s, 3);
				len += 3;
			}
		}

		return std::string(buffer, len);
	}

	/*
	 *	ԭʼ�������ת��׼����
	 *	������Ҫ��ԷǷ��º�Լ����
	 */
	static inline std::string rawFlatCodeToStdCode(const char* code, const char* exchg, const char* pid)
	{
		thread_local static char buffer[64] = { 0 };
		auto len = strlen(exchg);
		memcpy(buffer, exchg, len);
		buffer[len] = '.';
		len += 1;

		auto plen = strlen(pid);
		auto clen = strlen(code);

		if (strcmp(code, pid) == 0 || plen == 0)
		{
			memcpy(buffer + len, code, clen);
			len += clen;
			buffer[len] = '\0';
		}
		else
		{
			memcpy(buffer + len, pid, plen);
			len += plen;
			buffer[len] = '.';
			len += 1;

			memcpy(buffer + len, code, clen);
			len += clen;
			buffer[len] = '\0';
		}

		return buffer;
	}

	static inline bool isMonthlyCode(const char* code)
	{
		//using namespace boost::xpressive;
		//���3-6λ�������֣����Ƿ��º�Լ
		//static cregex reg_stk = cregex::compile("^.*[A-z|-]\\d{3,6}$");	//CFFEX.IO.2007
		//return 	regex_match(code, reg_stk);
		auto len = strlen(code);
		char state = 0;
		for (std::size_t i = 0; i < len; i++)
		{
			char ch = code[len - i - 1];
			if (0 <= state && state < 3)
			{
				if (!('0' <= ch && ch <= '9'))
					return false;

				state += 1;
			}
			else if (3 <= state && state < 6)
			{
				if ('0' <= ch && ch <= '9')
					state += 1;
				else if (('A' <= ch && ch <= 'z') || ch == '-')
				{
					state = 7;
					break;
				}
			}
			else if (state == 6)
			{
				if (('A' <= ch && ch <= 'z') || ch == '-')
				{
					state = 7;
					break;
				}
			}
		}

		return state == 7;
	}

	/*
	 *	�ڻ���Ȩ�����׼��
	 *	��׼�ڻ���Ȩ�����ʽΪCFFEX.IO2008.C.4300
	 *	-- ��ʱû�еط����� --
	 */
	static inline std::string rawFutOptCodeToStdCode(const char* code, const char* exchg)
	{
		using namespace boost::xpressive;
		/* �����������ʽ */
		static cregex reg_stk = cregex::compile("^[A-z]+\\d{4}-(C|P)-\\d+$");	//�н�������������ʽIO2013-C-4000
		bool bMatch = regex_match(code, reg_stk);
		if(bMatch)
		{
			std::string s = std::move(fmt::format("{}.{}", exchg, code));
			StrUtil::replace(s, "-", ".");
			return s;
		}
		else
		{
			//֣������������Ȩ�����ʽZC2010P11600

			//�ȴӺ���ǰ��λ��P��C��λ��
			std::size_t idx = strlen(code) - 1;
			for(; idx >= 0; idx--)
			{
				if(!isdigit(code[idx]))
					break;
			}
			
			std::string s = exchg;
			s.append(".");
			s.append(code, idx);
			s.append(".");
			s.append(&code[idx], 1);
			s.append(".");
			s.append(&code[idx + 1]);
			return s;
		}
	}

	/*
	 *	��׼��Լ����ת��������
	 */
	static inline std::string stdCodeToStdHotCode(const char* stdCode)
	{
		std::size_t idx = find(stdCode, '.', true);
		if (idx == std::string::npos)
			return "";		
		
		std::string stdWrappedCode;
		stdWrappedCode.resize(idx + strlen(SUFFIX_HOT) + 1);
		memcpy((char*)stdWrappedCode.data(), stdCode, idx);
		wt_strcpy((char*)stdWrappedCode.data()+idx, SUFFIX_HOT);
		return stdWrappedCode;
	}

	/*
	 *	��׼��Լ����ת����������
	 */
	static inline std::string stdCodeToStd2ndCode(const char* stdCode)
	{
		std::size_t idx = find(stdCode, '.', true);
		if (idx == std::string::npos)
			return "";

		std::string stdWrappedCode;
		stdWrappedCode.resize(idx + strlen(SUFFIX_2ND) + 1);
		memcpy((char*)stdWrappedCode.data(), stdCode, idx);
		wt_strcpy((char*)stdWrappedCode.data() + idx, SUFFIX_2ND);
		return stdWrappedCode;
	}

	/*
	 *	��׼�ڻ���Ȩ����תԭ����
	 *	-- ��ʱû�еط����� --
	 */
	static inline std::string stdFutOptCodeToRawCode(const char* stdCode)
	{
		std::string ret = stdCode;
		auto pos = ret.find(".");
		ret = ret.substr(pos + 1);
		if (strncmp(stdCode, "CFFEX", 5) == 0 || strncmp(stdCode, "DCE", 3) == 0)
			StrUtil::replace(ret, ".", "-");
		else
			StrUtil::replace(ret, ".", "");
		return ret;
	}

	static inline int indexCodeMonth(const char* code)
	{
		if (strlen(code) == 0)
			return -1;

		std::size_t idx = 0;
		std::size_t len = strlen(code);
		while(idx < len)
		{
			if (isdigit(code[idx]))
				return (int)idx;

			idx++;
		}
		return -1;
	}

	/*
	 *	��ȡ��׼�ڻ���Ȩ�������Ϣ
	 */
	static CodeInfo extractStdChnFutOptCode(const char* stdCode)
	{
		CodeInfo codeInfo;

		StringVector ay = StrUtil::split(stdCode, ".");
		wt_strcpy(codeInfo._exchg, ay[0].c_str());
		if(strcmp(codeInfo._exchg, "SHFE") == 0 || strcmp(codeInfo._exchg, "CZCE") == 0)
		{
			fmt::format_to(codeInfo._code, "{}{}{}", ay[1], ay[2], ay[3]);
		}
		else
		{
			fmt::format_to(codeInfo._code, "{}-{}-{}", ay[1], ay[2], ay[3]);
		}

		int mpos = indexCodeMonth(ay[1].c_str());

		if(strcmp(codeInfo._exchg, "CZCE") == 0)
		{
			memcpy(codeInfo._product, ay[1].c_str(), mpos);
			strcat(codeInfo._product, ay[2].c_str());
		}
		else if (strcmp(codeInfo._exchg, "CFFEX") == 0)
		{
			memcpy(codeInfo._product, ay[1].c_str(), mpos);
		}
		else
		{
			memcpy(codeInfo._product, ay[1].c_str(), mpos);
			strcat(codeInfo._product, "_o");
		}

		return codeInfo;
	}

	/*
	 *	�����׼�������Ϣ
	 */
	static CodeInfo extractStdCode(const char* stdCode)
	{
		//��Ȩ�Ĵ���������������һ�������Ե����ж�
		if(isStdChnFutOptCode(stdCode))
		{
			return extractStdChnFutOptCode(stdCode);
		}
		else
		{
			/*
			 *	By Wesley @ 2021.12.25
			 *	1���ȿ��ǲ���Q��H��β�ģ�����Ǹ�Ȩ���ȷ���Ժ����һ�γ���-1�����Ƶ�code����SSE.STK.600000Q
			 *	2���ٿ��ǲ��Ƿ��º�Լ������ǣ���product�ֶ�ƴ���·ݸ�code��֣�������⴦��������CFFEX.IF.2112
			 *	3����󿴿��ǲ���HOT��2ND��β�ģ�����ǣ���product������code����DCE.m.HOT
			 *	4����������ǣ���ԭ�����Ƶ����Σ���BINANCE.DC.BTCUSDT/SSE.STK.600000
			 */
			thread_local static CodeInfo codeInfo;
			codeInfo.clear();
			auto idx = StrUtil::findFirst(stdCode, '.');
			memcpy(codeInfo._exchg, stdCode, idx);

			auto idx2 = StrUtil::findFirst(stdCode + idx + 1, '.');
			if (idx2 == std::string::npos)
			{
				wt_strcpy(codeInfo._product, stdCode + idx + 1);

				//By Wesley @ 2021.12.29
				//��������εĺ�Լ���룬��OKEX.BTC-USDT
				//��Ʒ�ִ���ͺ�Լ����һ��
				wt_strcpy(codeInfo._code, stdCode + idx + 1);
			}
			else
			{
				memcpy(codeInfo._product, stdCode + idx + 1, idx2);
				const char* ext = stdCode + idx + idx2 + 2;
				std::size_t extlen = strlen(ext);
				char lastCh = ext[extlen - 1];
				if (lastCh == SUFFIX_QFQ || lastCh == SUFFIX_HFQ)
				{
					memcpy(codeInfo._code, ext, extlen - 1);
					codeInfo._exright = (lastCh == SUFFIX_QFQ) ? 1 : 2;
				}
				else if (extlen == 4 && '0' <= lastCh && lastCh <= '9')
				{
					//������һ����4λ���֣�˵���Ƿ��º�Լ
					//TODO: �������жϴ���һ�����裬���һλ�����ֵ�һ�����ڻ����º�Լ���Ժ���ܻ������⣬��ע��һ��
					//��ôcode�ü���Ʒ��id
					//֣�����õ�������һ�£����ֻ��hardcode��
					auto i = wt_strcpy(codeInfo._code, codeInfo._product);
					if (memcmp(codeInfo._exchg, "CZCE", 4) == 0)
						memcpy(codeInfo._code + i, ext + 1, extlen-1);
					else
						memcpy(codeInfo._code + i, ext, extlen);
				}
				else
				{
					codeInfo._hotflag = CodeHelper::isStdFutHotCode(stdCode) ? 1 : (CodeHelper::isStdFut2ndCode(stdCode) ? 2 : 0);
					if (codeInfo._hotflag == 0)
						wt_strcpy(codeInfo._code, ext);
					else
						wt_strcpy(codeInfo._code, codeInfo._product);
				}
			}			

			return codeInfo;
		}
	}
};

